package com.yuzhihao.writejvm.attributes.stack;

import com.yuzhihao.writejvm.attributes.stack.verification.VerificationTypeInfo;
import com.yuzhihao.writejvm.clazz.ClassInfo;
import com.yuzhihao.writejvm.format.PrintFormat;
import com.yuzhihao.writejvm.format.StackMapFrameEnum;
import com.yuzhihao.writejvm.read.Parse;
import com.yuzhihao.writejvm.read.ReadClassFileFatory;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.log4j.Log4j2;
import org.springframework.util.CollectionUtils;

import java.io.DataInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 255
 * @see  <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.4">jvmdoc</a>
 * @author yzh
 * @since 2021年12月14日13:32:39
 */
@Data
@Log4j2
@EqualsAndHashCode(callSuper = true)
public class FullFrame extends StackMapFrame{

    private int offset_delta_u2 = 2;

    private int offset_delta;

    private int number_of_locals_u2 = 2;

    private int number_of_locals;

    private List<VerificationTypeInfo> locals = new ArrayList<>();

    private int number_of_stack_items_u2 = 2;

    private int number_of_stack_items;

    private List<VerificationTypeInfo> stacks = new ArrayList<>();

    public FullFrame() {
        super();
        super.setSmfe(StackMapFrameEnum.full_frame);
    }

    @Override
    public void parse(DataInputStream dis, ClassInfo ci) {
        super.setCi(ci);
        try {
            this.offset_delta = Parse.byteToInt(this.offset_delta_u2, dis);

            this.number_of_locals = Parse.byteToInt(this.number_of_locals_u2, dis);
            int i = 0;
            while (i < this.number_of_locals){
                int value = Parse.byteToInt(VerificationTypeInfo.tag_u1, dis);
                VerificationTypeInfo local = ReadClassFileFatory.verification_instance(value);
                local.parse(dis,ci);
                this.locals.add(local);
                i++;
            }
            this.number_of_stack_items = Parse.byteToInt(this.number_of_stack_items_u2, dis);
            i = 0;
            while (i < this.number_of_stack_items){
                int value = Parse.byteToInt(VerificationTypeInfo.tag_u1, dis);
                VerificationTypeInfo stack = ReadClassFileFatory.verification_instance(value);
                stack.parse(dis,ci);
                this.stacks.add(stack);
                i++;
            }
        } catch (IOException e) {
            log.error(e);
        }
    }

    @Override
    public String print(String print) {
        StringBuilder builder = new StringBuilder();
        builder.append(print)
                .append(PrintFormat.EMPTY)
                .append("frame_type")
                .append(PrintFormat.EQUAL)
                .append(super.frame_type)
                .append(PrintFormat.EMPTY)
                .append("/* full_frame */")
                .append(System.lineSeparator());

        String format = print + PrintFormat.EMPTY +PrintFormat.EMPTY;
        builder.append(format)
                .append("offset_delta")
                .append(PrintFormat.EQUAL)
                .append(this.offset_delta)
                .append(System.lineSeparator());

        //locals
        if(!CollectionUtils.isEmpty(this.locals)){
            builder.append(format).append("locals").append(PrintFormat.EQUAL).append("[").append(PrintFormat.EMPTY);
            for (VerificationTypeInfo local : this.locals) {
                builder.append(local.print()).append(",").append(PrintFormat.EMPTY);
            }
            builder.delete(builder.length()-2,builder.length());
            builder.append(PrintFormat.EMPTY).append("]").append(System.lineSeparator());
        }
        //stack
        if(!CollectionUtils.isEmpty(this.stacks)){
            builder.append(format).append("stack").append(PrintFormat.EQUAL).append("[").append(PrintFormat.EMPTY);
            for (VerificationTypeInfo local : this.stacks) {
                builder.append(local.print()).append(",").append(PrintFormat.EMPTY);
            }
            builder.delete(builder.length()-2,builder.length());
            builder.append(PrintFormat.EMPTY).append("]").append(System.lineSeparator());
        }

        return builder.toString();
    }
}
